//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
#include <windows.h>
#include <iplcommon.h>
#include <bootpart.h>   // Needed for partition types...
#include <boot.h>
#include <bsp.h>

#include "BL_Battery.h"

#define DISCARD_SCAN_NUM 200   // At least 95 are required after keypad is initialized before it reads correctly

//------------------------------------------------------------------------------
// Global variables
//

// IPLCommon relies on the following when using bootpart, we use bootstore so values are ignored
UINT32 g_ulFlashBase        = 0;
UINT32 g_ulFlashLengthBytes = 0;
UINT32 g_ulBPartBase        = 0;    
UINT32 g_ulBPartLengthBytes  = 0;

UINT32 g_oalLogMask = 0;

BOOL g_bDebugSerialEnable = TRUE;

// --- These are used by iplcommon (end)   ---

// Needed by IPL drivers (display, keypad, ...) that use XLLP functions for initialization
UINT8 g_pMfpRmDb[XLLP_MFP_RM_DB_SIZE];
BOOL g_MfpRmDbInitialized = FALSE;

// External variables.
//
extern PFN_MessageHandler g_pfnMessageHandler;

// Function prototypes.
//
void OEMWriteDebugString(unsigned short *str);
void InitDebugSerial(UINT32 DbgSerPhysAddr, BOOL fUsePhysical);
static void OEMMessageHandler(IPL_MESSAGE_CODE MessageCode, LPWSTR pMessageString);


void main(void)
{
    //ldr     r4, =0x40100000                 
    //mov     r12, #0x33
    //strb    r12, [r4, #0]
    volatile UINT32 *pTmp = (volatile UINT32 *) 0x40700000;
    *pTmp = 'B';


    // Temporary move to here, should be in OEMIPLInit
    //InitDebugSerial((UINT32)MONAHANS_BASE_REG_PA_FFUART, TRUE);
    //g_pfnMessageHandler = OEMMessageHandler;
    // End

    //OEMWriteDebugString(L"+IPLMain called\r\n");
    // Call into the IPLcommon main routine.
    //
    //OEMWriteDebugString(L"IPLMain called\r\n");
    IPLmain();

    *pTmp = 'C';

    //OEMWriteDebugString(L"-IPLMain called\r\n");

}


static void OEMWriteHexLED(UINT32 ulPattern)
{
    // Stub.
    OEMWriteDebugString(L"OEMWriteHexLCD\r\n");
}


static void OEMMessageHandler(IPL_MESSAGE_CODE MessageCode, LPWSTR pMessageString)
{

    // Write debug message string.
    //
    OEMWriteDebugString(pMessageString);

}


///////////////////////////////////////////////////////////////////////////////
// Required OEM IPL routines.
///////////////////////////////////////////////////////////////////////////////

// --- These are used by iplcommon (start) ---
BOOLEAN OEMIPLInit(void)
{
    volatile unsigned int *pOSTRegs = (volatile unsigned int *) MONAHANS_BASE_REG_PA_OST;
    
    // Add a delay to prevent pre-existing serial data garble
    XllpOstDelayMilliSeconds((P_XLLP_OST_T) pOSTRegs, 250);

    // Initialize serial UART to use the STUART on the mainboard.
    InitDebugSerial((UINT32)MONAHANS_BASE_REG_PA_SIRUART, TRUE);

    // Messaging handler callback.
    //
    g_pfnMessageHandler = OEMMessageHandler;

    // Print banner
    OALMSG(1, (
        L"\r\nMicrosoft Windows CE IPL for PXA310 Littleton\r\n"
        L"Built %s at %s\r\n", TEXT(__DATE__), TEXT(__TIME__)
        ));

    /* TODO: Add battery charge for Micco here */
#if 0
    // Note: The battery charger is also enabled in EBoot.
    // This is redundent, but the battery charger must be enabled 
    // as early as possible to insure the battery doesn't run out 
    // of juice before we boot.  For a production implementation
    // without EBoot it should be done here.  Otherwise, if EBoot
    // is included with the system this may be removed.
    //
    // Detect a battery charging source if available
    EnableBatteryCharger(
        (XLLP_VUINT32_T *)MONAHANS_BASE_REG_PA_OST, 
        (XLLP_VUINT32_T *)MONAHANS_BASE_REG_PA_I2C, 
        (XLLP_VUINT32_T *)MONAHANS_BASE_REG_PA_MFP
        );
#endif

    // Notify user of power on, will display splash screen
    OEMBootNotify(BOOT_NOTIFY_POWERON, NULL, 0);

    return(TRUE);
}

BOOLEAN OEMGetUpdateMode(void)
{
    HANDLE hKeypad = NULL;
    BOOL fUpdateMode = FALSE;
    BOOL *pUpdateMode, *pColdBoot;
    BSP_ARGS *pArgs = (BSP_ARGS *)IMAGE_SHARE_ARGS_PA_START;
    int scanDiscards = DISCARD_SCAN_NUM;

    // Get the actual keypad status
    hKeypad = BootKeypadGetHandle(0);
    if (hKeypad == NULL ) 
    {
        OALMSG(OAL_ERROR, (L"ERROR: OEMGetUpdateMode: "
            L"Keypad driver returned failure to OEMGetUpdateMode\r\n"
            ));
    }

    // Grab BSP args
    pColdBoot = &pArgs->bFormatPartition;
    pUpdateMode = &pArgs->bUpdateMode;

    if (pColdBoot == NULL || pUpdateMode == NULL)
    {
        OALMSG(OAL_ERROR, (L"ERROR: OEMGetUpdateMode: "
            L"IPLBootMenu: Invalid OAL Args pointer(s)!\r\n"
            ));
    }

    // When a key is held from boot, the first several scans always return invalid
    while(scanDiscards-- > 0) {
        if (BootKeypadIsPressed(hKeypad, VK_TSOFT2)) { }
    }

    // Look for softkey 2 to confirm Cold boot
    if( BootKeypadIsPressed(hKeypad, VK_TSOFT2) )
    {
        *pColdBoot = BootNotifyConfirmErase();
        OALMSG(OAL_INFO, (L"INFO: OEMGetUpdateMode: "
            L"Cold boot - softkey 2 was held\r\n"
            ));
    }

    // Check to see if the OAL Args forced the update flag, and clear it
    // Continue, and let the user override these settings
    fUpdateMode = *pUpdateMode;
    *pUpdateMode = 0;

    // Look for softkey 1 to confirm ULDR boot 
    if( BootKeypadIsPressed(hKeypad, VK_TSOFT1) )
    {
        fUpdateMode = BootNotifyConfirmUldr();
        OALMSG(OAL_INFO, (L"INFO: OEMGetUpdateMode: "
            L"Update flag is set -softkey 1 was held \r\n"
            ));
    }

    // Last, check the update flag in Flash, which the user will not be able to override
    if(!fUpdateMode)
    {
        if(!BP_GetUpdateModeFlag(&fUpdateMode))
        {
            OALMSG(OAL_ERROR, (L"ERROR: OEMGetUpdateMode: "
                L"Failed to get persistent update flag\r\n"
                ));
            fUpdateMode = FALSE;
        }
        if (fUpdateMode)
        {
            OALMSG(OAL_INFO, (L"INFO: OEMGetUpdateMode: "
                L"Update Mode Persistent flag is set\r\n"
                ));
        }
    }

    if (fUpdateMode)
        OALMSG(1, (L"Launching the Update Loader...\r\n"));
    else
        OALMSG(1, (L"Launching the Kernel image ...\r\n"));

    return(fUpdateMode);
}


// Refer to OEMAddrTable
#define DEVICE_RAM_CA 0x80000000
#ifdef BSP_DDR128MB
#define DEVICE_RAM_PA 0xBC000000
#define DEVICE_RAM_SIZE 0x08000000
#else
#define DEVICE_RAM_PA 0xBC000000
#define DEVICE_RAM_SIZE 0x04000000
#endif
BOOLEAN OEMTranslateBaseAddress(UINT32 ulPartType, UINT32 ulAddr, UINT32 *pulTransAddr)
{
    BOOL rc = FALSE;
    RETAILMSG(1, (TEXT("OEMTranslateAddress:\r\n")));
    RETAILMSG(1, (TEXT("ulPartType(0x%x), ulAddr(0x%x), pulTransAddr(0x%x)\r\n"), ulPartType, ulAddr, pulTransAddr));
    if (pulTransAddr == NULL)
    {
        return rc;
    }

    // Strip cached/uncached bit
    ulAddr = ulAddr & ~OAL_MEMORY_CACHE_BIT;

    if (*pulTransAddr < DEVICE_RAM_CA || *pulTransAddr >= DEVICE_RAM_CA + DEVICE_RAM_SIZE)
        goto cleanUp;

    // Get final address
    *pulTransAddr = *pulTransAddr - DEVICE_RAM_CA + DEVICE_RAM_PA;
    rc = TRUE;

cleanUp:
    RETAILMSG(1, (
        L"-OEMTranslateBaseAddress(rc = %d, *pulTransAddr = 0x%x)\r\n", 
        rc, *pulTransAddr
    ));
    return rc;

}

void OEMLaunchImage(UINT32 ulLaunchAddr)
{
    typedef void (*PFN_LAUNCH)();

    RETAILMSG(1, (TEXT("OEMLanuchImage @ 0x%x\r\n"), ulLaunchAddr));
    OEMWriteDebugString(L"OEMLaunchImage called\r\n");
    //dumpData((unsigned char *) ulLaunchAddr, 0x1000, 0);

    // Since the MMU is off, we just need to jump to the address provided (it was translated by OEMTranslateBaseAddress).
    //
    ((PFN_LAUNCH)(ulLaunchAddr))();
}


// --- These are used by iplcommon (end)   ---

